# -*- coding: utf-8 -*-

from ir.ir_type import *
from ir.ir_ds import ITypeEnum, ITypeStruct, ITypeList
from ir.ir_ports import *
from ir.ir_node import *

from hbcxx.cxx_format import simcxx_add_include


def simcxx_get_pointer_type(raw_type: str):
    return '{} *'.format(raw_type)


def simcxx_remove_pointer_type(pointer_type: str):
    if pointer_type.endswith('*'):
        return pointer_type[:-1].strip()
    return pointer_type


def simcxx_convert_port(port_type: ITypePort):
    if isinstance(port_type, ITypePortOut):
        managed_type = simcxx_get_type(port_type.managed_type)
        cxx_port_type = 'PortOut<{}>'.format(managed_type)
        simcxx_add_include('"port.h"')
    elif isinstance(port_type, ITypePortIn):
        managed_type = simcxx_get_type(port_type.managed_type)
        cxx_port_type = 'PortIn<{}>'.format(managed_type)
        simcxx_add_include('"port.h"')
    elif isinstance(port_type, ITypePortInDirect):
        managed_type = simcxx_get_type(port_type.managed_type)
        cxx_port_type = 'PortInDirect<{}>'.format(managed_type)
        simcxx_add_include('"port.h"')
    elif isinstance(port_type, ITypeLanePort):
        cxx_port_type = port_type.name
        simcxx_add_include('"lane.h"')
    elif isinstance(port_type, ITypeChannelPort):
        cxx_port_type = port_type.name
        simcxx_add_include('"channel.h"')
    else:
        raise Exception('Unimplemented port type {}.'.format(type(port_type)))
    return simcxx_get_pointer_type(cxx_port_type)


def simcxx_get_type(ir_type):
    # Check the type.
    if ir_type == ITypeInt or ir_type == ITypeFloat or ir_type == ITypeBool:
        return ir_type.type
    if ir_type == ITypeComplex:
        # Once we convert complex type add global include.
        simcxx_add_include('<complex>')
        return 'std::complex<float>'
    if isinstance(ir_type, ITypeEnum) or isinstance(ir_type, ITypeStruct):
        return ir_type.name
    if isinstance(ir_type, ITypeList):
        return 'List<{}>'.format(simcxx_get_type(ir_type.managed_type))
    if isinstance(ir_type, ITypePort):
        return simcxx_convert_port(ir_type)
    if isinstance(ir_type, IVarReg):
        return simcxx_get_type(ir_type.type)
    if isinstance(ir_type, ITypeNode):
        return simcxx_get_pointer_type(ir_type.name)
    if isinstance(ir_type, ITypeChannel):
        return ir_type.name
    raise Exception('Unknown C++ type convert {}.'.format(type(ir_type)))